﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Twodi.Implemented;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Twodi.Input.Events;
using Twodi.Aspect.Interfaces;
using Twodi.Aspect.Implemented;

namespace Twodi.Camera
{
    public class Camera<T> : AbstractMappedContainer<T> where T : IBoundsAspect
    {
        protected Rectangle viewPort;

        protected IPositionAspect focused;

        public Camera(Rectangle viewPort, Game game)
            : base(game)
        {
            this.TransformationProperties = new CameraTransformationAspect<T>(this);
            this.CameraTransformation = TransformationProperties as CameraTransformationAspect<T>;

            this.ViewPort = viewPort;
            this.Size = new Vector2(viewPort.Width, viewPort.Height);
            this.Origin = this.Center;
            this.Position += this.Center;

            ContainerPreRender += (sender, e) =>
            {
                if (ValidSpriteBatch)
                {
                    SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null, CameraTransformation.CameraTransformation);
                }
            };

            ContainerPosRender += (sender, e) =>
            {
                if (ValidSpriteBatch)
                {
                    SpriteBatch.End();
                }
            };

            ContainerPreMouseHandle += (sender, e) =>
            {
                e.Input.Transform(Transformation);
            };

            ContainerPosMouseHandle += (sender, e) =>
            {
                e.Input.Transform(Inverted);
            };
        }

        public override void Update(GameTime gameTime)
        {
            if (Focused != null) FollowFocused();
            base.Update(gameTime);
        }

        protected virtual void FollowFocused()
        {
            this.Position = focused.PositionProperties.Position;
        }

        public CameraTransformationAspect<T> CameraTransformation
        {
            get;
            protected set;
        }

        public Rectangle ViewPort
        {
            get { return this.viewPort; }
            set
            {
                this.viewPort = value;
                onViewPortChanged(EventArgs.Empty);
            }
        }

        public IPositionAspect Focused
        {
            get { return this.focused; }
            set
            {
                this.focused = value;
                onFocusedChanged(EventArgs.Empty);
            }
        }

        public event EventHandler<EventArgs> ViewPortChanged;

        public event EventHandler<EventArgs> FocusedChanged;

        protected virtual void onViewPortChanged(EventArgs e)
        {
            if (ViewPortChanged != null)
            {
                ViewPortChanged(this, e);
            }
        }

        protected virtual void onFocusedChanged(EventArgs e)
        {
            if (FocusedChanged != null)
            {
                FocusedChanged(this, e);
            }
        }
    }

    public class CameraTransformationAspect<T> : DynamicTransformationAspect where T : IBoundsAspect
    {
        public CameraTransformationAspect(Camera<T> component)
            : base(component)
        {

        }

        public Matrix CameraTransformation
        {
            get
            {
                Matrix result = Matrix.Identity;

                if (RotationProperties != null)
                {
                    result *= Matrix.CreateTranslation(RotationProperties.Origin.X, RotationProperties.Origin.Y, 0) *
                            Matrix.CreateRotationZ(RotationProperties.Rotation);
                }

                if (ScaleProperties != null)
                {
                    result *= Matrix.CreateScale(ScaleProperties.Scale);
                }

                if (PositionProperties != null)
                {
                    result *= Matrix.CreateTranslation(-PositionProperties.Position.X, -PositionProperties.Position.Y, 0);
                }

                return result;
            }
        }
    }

    public class CameraContainerBehavior<T> : BasicContainerBehavior<T> where T : IBoundsAspect
    {
        public CameraContainerBehavior(Rectangle viewPort, IContainerAspect<T> component)
            : base(component)
        {
            this.ViewPort = viewPort;
        }

        public override void Draw(GameTime gameTime)
        {
            var visibleList = this.Component.ContainerProperties.Components.Where(
                b => b.BoundsProperties.Bounds.Intersects(ViewPort)
            );

            for (int i = 0; i < visibleList.Count(); i++)
            {
                IDrawable component = visibleList.ElementAt(i) as IDrawable;
                if (component != null)
                {
                    if (component.Visible) component.Draw(gameTime);
                }
            }
        }

        public Rectangle ViewPort
        {
            get;
            set;
        }
    }
}
